package com.gitee.fsmxm.thinking.in.concurrent.basic;

/**
 * 并发问题的源头
 * <p>
 * 1. 缓存导致的可见性问题  (使用 volatile synchronized 解决)
 * <p>
 * 2. 线程切换导致的原子性问题 (使用 synchronized 解决)
 * <p>
 * 3. 编译优化导致的有序性问题 (使用 volatile synchronized 解决)
 */
public class _01_TheSourceOfConcurrentBugProblems {

    /**
     * 举例： 缓存导致的可见性问题 和 线程切换导致的原子性问题
     */
    public static class Problem1 {
        private long count = 0;

        private void add10K() {
            int idx = 0;
            while (idx++ < 10000) {
                // 可能出现： ①线程切换导致的原子性问题 ②缓存导致的可见性问题
                // 解决方案： 对count变量使用volatile修饰仅解决了问题②，解决不了问题①；
                //          使用synchronized块可同时解决①②两个问题。
                // 解决方案二(无锁方案)： 使用AtomicLong
                count += 1;
            }
        }

        public static void main(String[] args) throws InterruptedException {
            Problem1 test = new Problem1();
            // 创建两个线程，执行add10K()操作
            Thread th1 = new Thread(test::add10K);
            Thread th2 = new Thread(test::add10K);
            // 启动两个线程
            th1.start();
            th2.start();
            // 等待两个线程执行结束
            th1.join();
            th2.join();
            System.out.println(test.count); // 输出结果可能小于20K
        }
    }


    /**
     * 编译优化导致的有序性问题
     * <p>
     * 我们以为的 new 操作应该是：
     * 1. 分配一块内存 M；
     * 2. 在内存 M 上初始化 Singleton 对象；
     * 3. 然后 M 的地址赋值给 instance 变量。
     * <p>
     * 但是实际上优化后的执行路径却是这样的：
     * 1. 分配一块内存 M；
     * 2. 将 M 的地址赋值给 instance 变量；
     * 3. 最后在内存 M 上初始化 Singleton 对象。
     * <p>
     * 因此，程序有可能出现NullPointer异常
     * 如何解决： 给instance加上volatile关键字 （jdk1.5之前可能不行，没给volatile加强）
     * <p>
     * 如何写单例： 静态内部类的方法；饿汉子式 都是比较安全的方式
     * 参见{@link com.gitee.fsmxm.thinking.in.concurrent.basic._04_Singleton_Samples}
     */
    public static class Problem2 {
        private Problem2() {
        }

        private static Problem2 instance;

        public static Problem2 getInstance() {
            if (instance == null) {
                synchronized (Problem2.class) {
                    if (instance == null)
                        instance = new Problem2();
                }
            }
            return instance;
        }
    }

}
